package models

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"

	"gitee.com/lzqysoft/ims/libs"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

type ImsProjectFlow struct {
	Id               string    `orm:"column(id);pk"`
	Type             int       `orm:"column(type);null"`
	Seq              int       `orm:"column(seq);null"`
	Remark           string    `orm:"column(remark);null"`
	Status           int       `orm:"column(status);null"`
	Isdelete         int       `orm:"column(isdelete);null"`
	Ctime            time.Time `orm:"column(ctime);type(timestamp with time zone);null"`
	Creater          string    `orm:"column(creater);null"`
	CId              string    `orm:"column(c_id);null"`
	Utime            time.Time `orm:"column(utime);type(timestamp with time zone);null"`
	Updater          string    `orm:"column(updater);null"`
	UId              string    `orm:"column(u_id);null"`
	OrgId            string    `orm:"column(org_id);null"`
	DeptId           string    `orm:"column(dept_id);null"`
	SequenceId       string    `orm:"column(sequence_id);null"`
	Name             string    `orm:"column(name);null"`
	Level            int       `orm:"column(level);null"`
	Source           int       `orm:"column(source);null"`
	Character        int       `orm:"column(character);null"`
	SubjectId        string    `orm:"column(subject_id);null"`
	SubjectName      string    `orm:"column(subject_name);null"`
	Start            time.Time `orm:"column(start);type(timestamp with time zone);null"`
	End              time.Time `orm:"column(end);type(timestamp with time zone);null"`
	TutorId          string    `orm:"column(tutor_id);null"`
	TutorName        string    `orm:"column(tutor_name);null"`
	WordUrl          string    `orm:"column(word_url);null"`
	Md5              string    `orm:"column(md5);null"`
	Content          string    `orm:"column(content);null"`
	Schedule         string    `orm:"column(schedule);null"`
	Budget           float64   `orm:"column(budget);null"`
	Used             float64   `orm:"column(used);null"`
	ProfessorGroupId string    `orm:"column(professor_group_id);null"`
	DeptName         string    `orm:"column(dept_name);null"`
	Desc             string    `orm:"column(desc);null"`
	Reason           string    `orm:"column(reason);null"`
	SheduleTable     string    `orm:"column(shedule_table);null"`
	Expect           string    `orm:"column(expect);null"`
	Groupdetail      string    `orm:"column(groupdetail);null"`
	Analysis         string    `orm:"column(analysis);null"`
	Benefit          string    `orm:"column(benefit);null"`
	TutorDetail      string    `orm:"column(tutor_detail);null"`
	Place            string    `orm:"column(place);null"`
	Step             int       `orm:"column(step);null"`
	No               string    `orm:"column(no);null"`
	ProvinceNo       string    `orm:"column(province_no);null"`
	ProjectType      int       `orm:"column(project_type);null"`
	Scheme           string    `orm:"column(scheme);null"`
	Score1           int       `orm:"column(score1);null"`
	Score2           int       `orm:"column(score2);null"`
	Score3           int       `orm:"column(score3);null"`
	ProjectId        string    `orm:"column(project_id)"`
	Result           int       `orm:"column(result)"`
}

func (t *ImsProjectFlow) TableName() string {
	return "ims_project_flow"
}

func init() {
	orm.RegisterModel(new(ImsProjectFlow))
}

func NewImsProjectFlow() *ImsProjectFlow {
	return &ImsProjectFlow{
		Id:       libs.UUID(),
		Status:   1,
		Isdelete: 0,
		Ctime:    time.Now(),
		Utime:    time.Now(),
	}
}

// AddImsProjectFlow insert a new ImsProjectFlow into database and returns
// last inserted Id on success.
func AddImsProjectFlow(m *ImsProjectFlow) (id int64, err error) {
	o := orm.NewOrm()
	id, err = o.Insert(m)
	return
}

// GetImsProjectFlowById retrieves ImsProjectFlow by Id. Returns error if
// Id doesn't exist
func GetImsProjectFlowById(id string) (v *ImsProjectFlow, err error) {
	o := orm.NewOrm()
	v = &ImsProjectFlow{Id: id}
	if err = o.Read(v); err == nil {
		return v, nil
	}
	return nil, err
}

// GetAllImsProjectFlow retrieves all ImsProjectFlow matches certain condition. Returns empty list if
// no records exist
func GetAllImsProjectFlow(query map[string]string, fields []string, sortby []string, order []string,
	offset int64, limit int64) (ml []interface{}, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(ImsProjectFlow))
	// query k=v
	for k, v := range query {
		// rewrite dot-notation to Object__Attribute
		k = strings.Replace(k, ".", "__", -1)
		if strings.Contains(k, "isnull") {
			qs = qs.Filter(k, (v == "true" || v == "1"))
		} else {
			qs = qs.Filter(k, v)
		}
	}
	// order by:
	var sortFields []string
	if len(sortby) != 0 {
		if len(sortby) == len(order) {
			// 1) for each sort field, there is an associated order
			for i, v := range sortby {
				orderby := ""
				if order[i] == "desc" {
					orderby = "-" + v
				} else if order[i] == "asc" {
					orderby = v
				} else {
					return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
			qs = qs.OrderBy(sortFields...)
		} else if len(sortby) != len(order) && len(order) == 1 {
			// 2) there is exactly one order, all the sorted fields will be sorted by this order
			for _, v := range sortby {
				orderby := ""
				if order[0] == "desc" {
					orderby = "-" + v
				} else if order[0] == "asc" {
					orderby = v
				} else {
					return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
		} else if len(sortby) != len(order) && len(order) != 1 {
			return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
		}
	} else {
		if len(order) != 0 {
			return nil, errors.New("Error: unused 'order' fields")
		}
	}

	var l []ImsProjectFlow
	qs = qs.OrderBy(sortFields...)
	if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
		if len(fields) == 0 {
			for _, v := range l {
				ml = append(ml, v)
			}
		} else {
			// trim unused fields
			for _, v := range l {
				m := make(map[string]interface{})
				val := reflect.ValueOf(v)
				for _, fname := range fields {
					m[fname] = val.FieldByName(fname).Interface()
				}
				ml = append(ml, m)
			}
		}
		return ml, nil
	}
	return nil, err
}

// UpdateImsProjectFlow updates ImsProjectFlow by Id and returns error if
// the record to be updated doesn't exist
func UpdateImsProjectFlowById(m *ImsProjectFlow) (err error) {
	o := orm.NewOrm()
	v := ImsProjectFlow{Id: m.Id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Update(m); err == nil {
			fmt.Println("Number of records updated in database:", num)
		}
	}
	return
}

// DeleteImsProjectFlow deletes ImsProjectFlow by Id and returns error if
// the record to be deleted doesn't exist
func DeleteImsProjectFlow(id string) (err error) {
	o := orm.NewOrm()
	v := ImsProjectFlow{Id: id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Delete(&ImsProjectFlow{Id: id}); err == nil {
			fmt.Println("Number of records deleted in database:", num)
		}
	}
	return
}

func AutoGenerateImsProjectFlowByImsProject(user *User, project *ImsProject) (*ImsProjectFlow, error) {
	o := orm.NewOrm()
	pf := NewImsProjectFlow()
	pf.Type = 0
	pf.Seq = project.Seq
	pf.Remark = project.Remark
	pf.Creater = user.Abbr
	pf.CId = user.Id
	pf.Updater = user.Abbr
	pf.UId = user.Id
	pf.OrgId = project.OrgId
	pf.DeptId = project.DeptId
	pf.SequenceId = project.SequenceId
	pf.ProjectId = project.Id
	pf.Name = project.Name
	pf.Level = project.Level
	pf.Source = project.Source
	pf.Character = project.Character
	pf.SubjectId = project.SubjectId
	pf.SubjectName = project.SubjectName
	pf.Start = project.Start
	pf.End = project.End
	pf.TutorId = project.TutorId
	pf.TutorName = project.TutorName
	pf.WordUrl = project.WordUrl
	pf.Md5 = project.Md5
	pf.Result = 0
	_, err := o.Insert(pf)
	//beego.Debug("org, dept: ", project.OrgId, project.DeptId)
	//beego.Debug("org, dept: ", pf.OrgId, pf.DeptId)
	return pf, err
}

func AutoGenerateImsProjectFlowByImsProject1(user *User, project *ImsProject) (*ImsProjectFlow, error) {
	o := orm.NewOrm()
	pf := NewImsProjectFlow()
	pf.Type = 1
	pf.Seq = project.Seq
	pf.Remark = project.Remark
	pf.Creater = user.Abbr
	pf.CId = user.Id
	pf.Updater = user.Abbr
	pf.UId = user.Id
	pf.OrgId = project.OrgId
	pf.DeptId = project.DeptId
	pf.SequenceId = project.SequenceId
	pf.ProjectId = project.Id
	pf.Name = project.Name
	pf.Level = project.Level
	pf.Source = project.Source
	pf.Character = project.Character
	pf.SubjectId = project.SubjectId
	pf.SubjectName = project.SubjectName
	pf.Start = project.Start
	pf.End = project.End
	pf.TutorId = project.TutorId
	pf.TutorName = project.TutorName
	pf.WordUrl = project.WordUrl
	pf.Md5 = project.Md5
	pf.Result = 0
	_, err := o.Insert(pf)
	//beego.Debug("org, dept: ", project.OrgId, project.DeptId)
	//beego.Debug("org, dept: ", pf.OrgId, pf.DeptId)
	return pf, err
}
func AutoGenerateImsProjectFlowByImsProject2(user *User, project *ImsProject) (*ImsProjectFlow, error) {
	o := orm.NewOrm()
	pf := NewImsProjectFlow()
	pf.Type = 2
	pf.Seq = project.Seq
	pf.Remark = project.Remark
	pf.Creater = user.Abbr
	pf.CId = user.Id
	pf.Updater = user.Abbr
	pf.UId = user.Id
	pf.OrgId = project.OrgId
	pf.DeptId = project.DeptId
	pf.SequenceId = project.SequenceId
	pf.ProjectId = project.Id
	pf.Name = project.Name
	pf.Level = project.Level
	pf.Source = project.Source
	pf.Character = project.Character
	pf.SubjectId = project.SubjectId
	pf.SubjectName = project.SubjectName
	pf.Start = project.Start
	pf.End = project.End
	pf.TutorId = project.TutorId
	pf.TutorName = project.TutorName
	pf.WordUrl = project.WordUrl
	pf.Md5 = project.Md5
	pf.Result = 0
	_, err := o.Insert(pf)
	//beego.Debug("org, dept: ", project.OrgId, project.DeptId)
	//beego.Debug("org, dept: ", pf.OrgId, pf.DeptId)
	return pf, err
}
func AutoGenerateImsProjectFlowByImsProject3(user *User, project *ImsProject) (*ImsProjectFlow, error) {
	o := orm.NewOrm()
	pf := NewImsProjectFlow()
	pf.Type = 3
	pf.Seq = project.Seq
	pf.Remark = project.Remark
	pf.Creater = user.Abbr
	pf.CId = user.Id
	pf.Updater = user.Abbr
	pf.UId = user.Id
	pf.OrgId = project.OrgId
	pf.DeptId = project.DeptId
	pf.SequenceId = project.SequenceId
	pf.ProjectId = project.Id
	pf.Name = project.Name
	pf.Level = project.Level
	pf.Source = project.Source
	pf.Character = project.Character
	pf.SubjectId = project.SubjectId
	pf.SubjectName = project.SubjectName
	pf.Start = project.Start
	pf.End = project.End
	pf.TutorId = project.TutorId
	pf.TutorName = project.TutorName
	pf.WordUrl = project.WordUrl
	pf.Md5 = project.Md5
	pf.Content = project.Content
	pf.Schedule = project.Schedule

	pf.Result = 0
	_, err := o.Insert(pf)
	//beego.Debug("org, dept: ", project.OrgId, project.DeptId)
	//beego.Debug("org, dept: ", pf.OrgId, pf.DeptId)
	return pf, err
}

// 当projectflow 的 project_id 与 type 一致事，即认为该项目已经申报
func CheckImsProjectFlowCountByImsProjectIdAndType(projectId string, ptype int64) (int64, error) {
	o := orm.NewOrm()
	sum, err := o.QueryTable(new(ImsProjectFlow)).Filter("Isdelete", 0).
		Filter("ProjectId", projectId).Filter("Type", ptype).Count()
	if err != nil {
		beego.Debug(err)
		return 0, err
	}
	return sum, nil
}

func GetImsProjectDatasByImsProjectIdAndType(id string, ftype int, user *User) (res map[string]interface{}, err error) {
	beego.Debug(id, ftype)
	res = make(map[string]interface{})
	pf, err := getImsProjectFlowByIdAndType(id, ftype)
	if err != nil {
		beego.Debug(err)
		return
	}
	res["projectFlow"] = pf
	flowInstance, err := GetFlowinstanceBybusyRewrite(pf.TableName(), pf.Id)
	if err != nil {
		beego.Debug(err)
		return
	}
	res["flowInstance"] = flowInstance
	stateinstance, err := GetStateByFlowInsReWrite(flowInstance.Id)
	if err != nil {
		beego.Debug(err)
		return
	}
	res["stateInstance"] = stateinstance
	//transtationInstances, err := GetTransitionByCurrentStateInstance(flowInstance.Currentstate, user.Id)
	transtationInstances, err := GetTransitionByUserId(flowInstance.Id, user.Id)
	if err != nil {
		beego.Debug(err)
		return
	}
	res["transtationInstance"] = transtationInstances

	return
}
func GetValidProjectFlow(projectId string) (*ImsProjectFlow, error) {
	o := orm.NewOrm()
	m := &ImsProjectFlow{ProjectId: projectId, Status: 2}
	if err := o.Read(m, "ProjectId", "Status"); err != nil {
		beego.Debug(err)
		return nil, err
	}
	return m, nil
}
func GetImsProjectsData(projectFlowId string, ftype int, user *User, bname string) (res map[string]interface{}, err error) {
	beego.Debug(projectFlowId, ftype)
	res = make(map[string]interface{})
	//currentFlow, err := getImsProjectFlowByIdAndType(projectFlowId, ftype)
	//if err != nil {
	//	beego.Debug(err)
	//	return
	//}
	currentProject, err := GetImsProjectById(projectFlowId)
	if err != nil {
		beego.Debug(err)
		return
	}
	// 查询当前正在流转中的流程

	//GetStateInstancesChangeBusy(projectFlowId, libs.GetBusyName(ftype))
	// TODO 根据userID 和busyname 查询当前stateins和tranins
	//approveId := beego.AppConfig.String("ftype_id_0")
	//checkId := beego.AppConfig.String("ftype_id_1")
	//finishId := beego.AppConfig.String("ftype_id_2")
	//changeId := beego.AppConfig.String("ftype_id_3")
	//setUp
	//setUp, _ := GetStateInstancesInstanceByProjectIdAndType(currentProject.Id, approveId)
	//check, _ := GetStateInstancesInstanceByProjectIdAndType(currentProject.Id, checkId)
	//finish, _ := GetStateInstancesInstanceByProjectIdAndType(currentProject.Id, finishId)
	//result, _ := GetStateInstancesInstanceByProjectIdAndType(currentProject.Id, changeId)
	//changes, err := GetStateInstancesChangeBusy(currentProject.Id, "ims_project_flow")
	busyId := ""
	switch bname {
	case "ims_project":
		busyId = currentProject.Id
	case "ims_project_medium_term":
		busyId = currentProject.ImsProjectMediumTerm[0].Id
	case "ims_project_finish_report":
		busyId = currentProject.ImsProjectFinishReport[0].Id
	case "ims_project_flow":
		busyId = currentProject.Id
	}
	ins := GetCurrentStateIns(busyId, user.Id, bname)
	transitionInstances, err := GetTransitionByCurrentStateInstance(ins.Id, user.Id)
	beego.Debug(transitionInstances)
	beego.Debug(err)
	res["transtationInstance"] = transitionInstances
	//res["SetUp"] = setUp
	//res["Check"] = check
	//res["Finish"] = finish
	//res["Result"] = result
	//res["Change"] = changes

	res["CurrentStateIns"] = ins

	return
}

func getImsProjectFlowByIdAndType(id string, ftype int) (*ImsProjectFlow, error) {
	o := orm.NewOrm()
	res := &ImsProjectFlow{Id: id}
	if err := o.Read(res); err != nil {
		return nil, err
	}
	return res, nil
}
func GetCurrentStateIns(busyId, userId, busyName string) *Stateinstance {
	o := orm.NewOrm()
	m := &Stateinstance{}
	qs := o.QueryTable(new(Stateinstance)).Filter("UserId", userId).Filter("Busyname", busyName).Filter("Busyid", busyId)
	qs = qs.Filter("Isdelete", 0).Filter("Status", 1)
	err := qs.One(m)
	if err != nil {
		beego.Debug(err)
		return nil
	}
	return m
}
func QueryProjectFlowByTypeAndResult(ptype, result, status int, userId string) ([]ImsProjectFlow, error) {
	o := orm.NewOrm()
	var l []ImsProjectFlow
	qs := o.QueryTable(new(ImsProjectFlow)).Filter("Type", ptype).Filter("CId", userId)
	if status == 2 {
		_, err := qs.All(&l)
		return l, err
	} else {
		qs = qs.Filter("Status", status).Filter("Result", result)
		_, err := qs.All(&l)
		return l, err
	}
}

// GetStateInstancesInstanceByProjectIdAndType
// 根据项目Id和项目审批类型，获取审核的节点state
func GetStateInstancesInstanceByProjectIdAndType(projectId string, flowId string) ([]Stateinstance, error) {
	o := orm.NewOrm()
	m := &ImsProject{}
	//if err := o.Read(m, "ProjectId", "Type"); err != nil {
	//	beego.Debug(err)
	//	return nil, err
	//}
	if err := o.QueryTable(new(ImsProject)).Filter("Id", projectId).One(m); err != nil {
		beego.Debug(err)
		return nil, err
	}
	f, _ := GetFlowById(flowId)
	flow := &Flowinstance{Busyname: m.TableName(), Busyid: m.Id, Flow: f}
	if err := o.Read(flow, "Busyname", "Busyid", "Flow"); err != nil {
		beego.Debug(err)
		return nil, err
	}
	if si, err := GetStateByFlowInsReWrite(flow.Id); err != nil {
		beego.Debug(err)
		return nil, err
	} else {
		return si, err
	}
}

// GetStateInstancesChangeBusy
// 根据项目id, busynamew查询flowsinstances
func GetStateInstancesChangeBusy(projectId, busyname string) ([]Flowinstance, error) {
	o := orm.NewOrm()
	var list orm.ParamsList
	if _, err := o.QueryTable(new(ImsProjectFlow)).Filter("ProjectId", projectId).Filter("Isdelete", 0).OrderBy("Ctime").
		ValuesFlat(&list, "Id"); err != nil {
		beego.Debug(err)
		return nil, err
	}
	var l []Flowinstance
	if len(list) <= 0 {
		return l, nil
	}

	if _, err := o.QueryTable(new(Flowinstance)).Filter("Busyname", busyname).Filter("Busyid__in", list).
		Filter("Isdelete", 0).OrderBy("Ctime").All(&l); err != nil {
		beego.Debug(err)
		return l, err
	}

	for k, _ := range l {
		if _, err := o.LoadRelated(&l[k], "Stateinstance"); err != nil {
			beego.Debug(err)
		}
	}

	return l, nil
}
